package virtualReader;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.ArrayList;

import java.util.List;
import java.util.Random;
import java.util.concurrent.TimeUnit;
import java.util.logging.Logger;

import javax.xml.bind.DatatypeConverter;

import com.google.protobuf.ByteString;
import com.google.protobuf.InvalidProtocolBufferException;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.util.HashedWheelTimer;
import io.netty.util.Timeout;
import io.netty.util.Timer;
import io.netty.util.TimerTask;
import rdp_msg.Rdp;
import rdp_msg.Rdp.AntPara;
import rdp_msg.Rdp.ConfigPara;
import rdp_msg.Rdp.ProtocalPara;
import rdp_msg.Rdp.rdp_request;
import rdp_msg.Rdp.rdp_response;
import rdp_msg.Rdp.rdp_response.HeadType;
import rdp_msg.Rdp.rdp_response.ReadResultRara;
import rdp_msg.Rdp.rdp_response.TagReportDataMsg;
import rdp_msg.Rdp.rdp_response.TagReportDataPara;
import rdp_msg.Rdp.rdp_response.TagReportDataMsg.Builder;
import java.util.Properties;

// TODO: Auto-generated Javadoc
/**
 * The Class VirtualReader.
 */
public class VirtualReader {
	protected String ip;	
	protected int port = 5084;
	protected EventLoopGroup bossGroup = new NioEventLoopGroup();
	protected EventLoopGroup workerGroup = new NioEventLoopGroup();
	protected ChannelFuture future;

	/** The log. */
	protected Logger log = Logger.getLogger("VirtualReader");
	/** The response. */
	protected rdp_request request;
	protected rdp_response response;

	protected byte[] setConfigAck;
	protected byte[] startReaderWorkAck;
	protected byte[] stopReaderWorkAck;
	private int length,opType;
	private  Properties properties;
	private FileInputStream fi;
	
	
	static Timer timer = new HashedWheelTimer(50L, TimeUnit.MILLISECONDS, 512);

	/**
	 * Instantiates a new virtual reader.
	 *
	 * @param port
	 *            the port
	 */
	public VirtualReader(String ip,int port) {
		super();
		this.ip=ip;
		this.port = port;
		// 初始化Ack
		rdp_response.Builder ack = rdp_response.newBuilder();
		ack.setType(rdp_response.HeadType.setConfigAck); 
		setConfigAck = ack.build().toByteArray();
		ack.setType(rdp_response.HeadType.startWorkAck);
		startReaderWorkAck = ack.build().toByteArray();
		ack.setType(rdp_response.HeadType.stopWorkAck);
		stopReaderWorkAck = ack.build().toByteArray();
		
		properties = PropertyClass.getInstance().getProperties();
		
		
	}

	/**
	 * Start.
	 *
	 * @throws Exception
	 *             the exception
	 */
	public void start() throws Exception {
		ServerBootstrap bootstrap = new ServerBootstrap();
		bootstrap.group(bossGroup, workerGroup).channel(NioServerSocketChannel.class)
				.childHandler(new VrChannelInitializer(this));		
		future=bootstrap.bind(port).sync();
		log.info("bind:" + ip);
	}

	/**
	 * Stop.
	 */
	public void stop() {
		try {
			future.channel().closeFuture().sync();
			future = null;	
		} catch (InterruptedException e) {
			log.warning("failed to close port:" + ip);
		} finally {
			workerGroup.shutdownGracefully();
			bossGroup.shutdownGracefully();
			log.info("close server :" + ip);
		}
	}

	/**
	 * Write.
	 *
	 * @param msgBytes
	 *            the msg bytes
	 */
	public void write(ChannelHandlerContext ctx,byte[] msgBytes) {
		if (null == future) {
			log.warning(ip + " channel is null!");
			return;
		}
		ByteBuf outBuf = Unpooled.buffer(msgBytes.length + 4);
		outBuf.writeInt(msgBytes.length);
		outBuf.writeBytes(msgBytes);
		ChannelFuture f = ctx.writeAndFlush(outBuf);
		try {
			f.await();
			if (!f.isSuccess()) {
				log.warning(ip + " send message failure!");
			}else{
				log.info(ip + " send message!");
			}
		} catch (InterruptedException e) {
			log.warning(e.getMessage());
		}
	}

	/**
	 * Read.
	 *
	 * @param msgBytes
	 *            the msg bytes
	 */
	public void read(ChannelHandlerContext ctx,byte[] msgBytes) {
		log.info(ip + " read byte[]=" + DatatypeConverter.printHexBinary(msgBytes));
		try {
			request = Rdp.rdp_request.parseFrom(msgBytes);
			switch (request.getType()) {
			case setConfig:
				log.info("setConfig" + ip);
				doSetConfigAck(ctx);
				break;
			case startReaderWork:
				log.info("startReaderWork" + ip);
				doStartReaderWorkAck(ctx);
				break;
			case keepAliveAck:
				log.info("keepAliveAck" + ip);
				break;
			case stopReaderWork:
				log.info("stopReaderWork" + ip);
				doStopReaderWorkAck(ctx);
				break;
			default:
				log.warning("unknown msg:" + ip);
			}
		} catch (InvalidProtocolBufferException e) {
			log.warning(e.getMessage());
		}
	}

	private void doStopReaderWorkAck(ChannelHandlerContext ctx) {
		write(ctx,this.stopReaderWorkAck);
		// 停止模拟report上报
	}

	private void doStartReaderWorkAck(ChannelHandlerContext ctx) {
		write(ctx,this.startReaderWorkAck);
		doReport(ctx);
	}

	private void doSetConfigAck(ChannelHandlerContext ctx) {
		ConfigPara conf = request.getConfig();
		if(conf.getProtocal(0).getReadCount()!=0){
			length= conf.getProtocal(0).getRead(0).getCount();
			System.out.println("read length is:"+length);
			opType = request.getConfig().getStopTrigger().getType();
			System.out.println("opType is :"+opType);
		}
		
		write(ctx,this.setConfigAck);
		// 可根据配置要求设计模拟report数据
	}
	
	private void addTask(TimerTask task){
		timer.newTimeout(task, 5, TimeUnit.SECONDS);
		
	}
	
	
	private  String randomHexString(int len)  {  
	    try {  
	        StringBuffer result = new StringBuffer();  
	        for(int i=0;i<len;i++) {  
	            result.append(Integer.toHexString(new Random().nextInt(16)));  
	        }  
	        return result.toString().toUpperCase();  
	          
	    } catch (Exception e) {  
	        // TODO: handle exception  
	        e.printStackTrace();  
	          
	    }  
	    return null;  
	      
	}  
	private void singleWrite(ChannelHandlerContext ctx){
		rdp_response.Builder res = rdp_response.newBuilder();
		res.setType(HeadType.reportTagData);
		Builder tagm = TagReportDataMsg.newBuilder();
		rdp_response.TagReportDataPara.Builder tdp = TagReportDataPara.newBuilder();
		tdp.setAntId(1);
		ByteString epc = ByteString.copyFromUtf8(getEPC());
			
		tdp.setEpc(epc);
		tdp.setProtocalType(1);
		tdp.setTagCount(1);
		
		
	
		if(length != 0){
			rdp_response.ReadResultRara.Builder rrr = ReadResultRara.newBuilder();
			rrr.setOpid(1);
			ByteString readData = ByteString.copyFromUtf8(randomHexString(length*16));
			rrr.setReadData(readData);
			rrr.setResult(0);
			tdp.addReadResult(rrr);
		}
		
		tdp.setTime(System.currentTimeMillis());
	
		tagm.addTagData(tdp);
		res.setTagReportData(tagm);
		write(ctx,res.build().toByteArray());
		
	}
	private String getEPC(){
		if(properties.getProperty("isRandomEPC").equals("1")){
			return generateEPC();
		}
		return epc10();
		
		
	}
	
	private  String generateEPC(){
		String head = "55916007";
		ArrayList<Integer> epcList = new ArrayList<Integer>();
		StringBuilder s = new StringBuilder();
		for (int i = 0;i<7;i++){
			epcList.add(new Random().nextInt(16));
		}
		int check = 0;
		for (int i = 0;i<epcList.size();i++){
			check^=epcList.get(i);
			s.append(String.format("%02x",epcList.get(i)));
		}
		return head+s+String.format("%02x",check);
		
	}
	
	private  String epc10(){
		ArrayList<String> epcList = new ArrayList<String>();
		epcList.add("559160070a080a010f030603");
		epcList.add("559160070c060c0a0b0c0902");
		epcList.add("55916007080b0c0d01060306");
		epcList.add("559160070c060d0e0d030106");
		epcList.add("559160070b07040d03010304");
		epcList.add("55916007080e050e0d0f0609");
		epcList.add("559160070c080409050f0704");
		epcList.add("559160070e0e0d0a050f0805");
		epcList.add("559160070c0f02040407080e");
		epcList.add("559160070e0909080e000e06");
		return epcList.get(new Random().nextInt(10));
		
		
	}
	
	
	private void doReport(final ChannelHandlerContext ctx) {
		// 循环上报
		if (0 == opType) {
			TimerTask task = new TimerTask() {
				public void run(Timeout timeout) throws Exception {
					singleWrite(ctx);
					// 任务执行完成后再把自己添加到任务solt上
					addTask(this);
				}
			};
			addTask(task);
		}
		//单次上报
		else
			singleWrite(ctx);
	

	}
}
